#include "peg.h"

#include <QPainter>
#include <qmath.h>
#include <QGraphicsSceneMouseEvent>
#include <assert.h>
#include <QCursor>
#include <QDebug>

#include "hole.h"
#include "gameboard.h"

Peg::Peg(Hole *hole) :
    QGraphicsItem(), m_radius(hole->radius()),
    m_hole(NULL)
{
    setHole(hole);
    hole->setPeg(this);
    setPos(hole->pos());

    setFlag(QGraphicsItem::ItemIsMovable, true);
    setFlag(ItemSendsScenePositionChanges, true);
    setAcceptedMouseButtons(Qt::LeftButton);

    // pegs are always above holes
    setZValue(0);
}

Hole *Peg::setHole(Hole *hole)
{
    Hole *old_hole = m_hole;
    m_hole = hole;
    return old_hole;
}

Hole *Peg::hole() const
{
    return m_hole;
}

QRectF Peg::boundingRect() const
{
    const qreal diameter = 2 * m_radius;
    return QRectF(-m_radius - m_outline_width / 2,
                  -m_radius - m_outline_width / 2,
                  diameter + m_outline_width,
                  diameter + m_outline_width);
}

void Peg::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
    QPen pen;
    QRadialGradient radialGradient(boundingRect().center(),
                                   m_radius,
                                   boundingRect().center());
    radialGradient.setColorAt(0.001, Qt::white);
    radialGradient.setColorAt(0.2, Qt::green);
    radialGradient.setColorAt(1.0, Qt::black);
    QBrush brush(radialGradient);

    pen.setStyle(Qt::SolidLine);
    pen.setWidth(m_outline_width);
    painter->setBrush(brush);
    painter->setPen(pen);
    painter->setRenderHint(QPainter::Antialiasing, true);
    painter->drawEllipse(boundingRect());
//    painter->drawRect(boundingRect());
}

QPainterPath Peg::shape() const
{
    QPainterPath path;
    path.addEllipse(boundingRect());
    return path;
}

void Peg::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
    // be above all other pegs
    setZValue(1);

    setCursor(QCursor(Qt::ClosedHandCursor));

    QGraphicsItem::mousePressEvent(event);
}

void Peg::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
{
    QList<QGraphicsItem*> collidingItemsList = collidingItems();
    QVector<QPair<QGraphicsItem*, qreal>> distances;

    Hole *min_item = NULL;
    qreal min_distance = 0;
    for (QGraphicsItem *item : collidingItemsList) {
        Hole *hole = dynamic_cast<Hole*>(item);
        if (hole && !hole->havePeg()
                 && isFeasibleHole(this->hole(), hole)) {
            QPointF disp = item->pos() - pos();
            qreal distance = qSqrt(disp.x() * disp.x()
                                   + disp.y() * disp.y());
            if (min_item) {
                if (min_distance > distance) {
                    min_distance = distance;
                    min_item = hole;
                }
            } else {
                min_distance = distance;
                min_item = hole;
            }
        }
    }

    if (min_item) {
        Hole *old_hole = hole();
        if (((GameBoard*)scene())->movePeg(hole()->boardPos(),
                                            min_item->boardPos())) {
            removeJumpedPeg(old_hole, min_item);
        } else {
            qDebug() << "Error: Can't move peg";
        }
    } else {
        setPos(hole()->pos());
    }
    ((GameBoard*)scene())->printBoard();

    // same as all other pegs
    setZValue(0);

    unsetCursor();

    QGraphicsItem::mouseReleaseEvent(event);
}

bool Peg::isFeasibleHole(const Hole *source, const Hole *target) const
{
    QPoint sourcePos = source->boardPos();
    QPoint targetPos = target->boardPos();

    QPoint displacement = QPoint(qAbs(sourcePos.x() - targetPos.x()),
                                 qAbs(sourcePos.y() - targetPos.y()));
    int requredDistance = 2;

    QPoint jumpedDisplacement = (sourcePos - targetPos) / 2;
    QPoint jumpedPos = sourcePos - jumpedDisplacement;

    // check feasibility conditions: hole orthogonality and distance
    bool orthogonal = sourcePos.x() == targetPos.x()
                      || sourcePos.y() == targetPos.y();
    bool distant = displacement.x() == requredDistance
                   || displacement.y() == requredDistance;
    // TODO: can we jump over the empty hole?
    bool jumpOver = ((GameBoard*)scene())->hole(jumpedPos)->havePeg();

    return orthogonal && distant && jumpOver;
}

void Peg::removeJumpedPeg(const Hole *source, const Hole *target) const
{
    QPoint sourcePos = source->boardPos();
    QPoint targetPos = target->boardPos();

    QPoint displacement = (sourcePos - targetPos) / 2;
    QPoint jumpedPos = sourcePos - displacement;

    ((GameBoard*)scene())->removePegFromTheBoard(jumpedPos);
}
